package redis

import (
	"errors"
	"fmt"
	"github.com/go-redis/redis"
	"sync"
)

//在某些场景下，我们除了要使用MULTI/EXEC命令外，还需要配合使用WATCH命令。
//在用户使用WATCH命令监视某个键之后，直到该用户执行EXEC命令的这段时间里，如果有其他用户抢先对被监视的键进行了替换、更新、删除等操作，
//那么当用户尝试执行EXEC的时候，事务将失败并返回一个错误，用户可以根据这个错误选择重试事务或者放弃事务。

//官方文档中使用GET和SET命令以事务方式递增Key的值的示例：
func WatchExample1() {
	const routineCount = 100

	increment := func(key string) error {
		txf := func(tx *redis.Tx) error {
			// 获得当前值或零值
			n, err := tx.Get(key).Int()
			if err != nil && err != redis.Nil {
				return err
			}

			// 实际操作（乐观锁定中的本地操作）
			n++

			// 仅在监视的Key保持不变的情况下运行
			_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
				// pipe 处理错误情况
				pipe.Set(key, n, 0)
				return nil
			})
			return err
		}

		for retries := routineCount; retries > 0; retries-- {
			err := rdb.Watch(txf, key)
			if err != redis.TxFailedErr {
				return err
			}
			// 乐观锁丢失
		}
		return errors.New("increment reached maximum number of retries")
	}

	var wg sync.WaitGroup
	wg.Add(routineCount)
	for i := 0; i < routineCount; i++ {
		go func() {
			defer wg.Done()

			if err := increment("counter3"); err != nil {
				fmt.Println("increment error:", err)
			}
		}()
	}
	wg.Wait()

	n, err := rdb.Get("counter3").Int()
	fmt.Println("ended with", n, err)
}

//func WatchExample2() {
//	// 监视watch_count的值，并在值不变的前提下将其值+1
//	key := "watch_count"
//	err = client.Watch(func(tx *redis.Tx) error {
//		n, err := tx.Get(key).Int()
//		if err != nil && err != redis.Nil {
//			return err
//		}
//		_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
//			pipe.Set(key, n+1, 0)
//			return nil
//		})
//		return err
//	}, key)
//}
